package com.example.filesftp.ftptransfer.service.impl;

import com.example.filesftp.config.NybgftpConfig;
import com.example.filesftp.ftptransfer.domain.entity.FileTransferConfig;
import com.example.filesftp.ftptransfer.service.FileSftpService;
import com.example.filesftp.ftptransfer.util.AESEncryption;
import com.example.filesftp.ftptransfer.util.FtpUtil;
import com.example.filesftp.ftptransfer.util.JsonFileUtil;
import com.example.filesftp.ftptransfer.util.SftpUtil;
import com.example.filesftp.job.FileTransferJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author chenhuan
 */
@Slf4j
@Service
//@RefreshScope
@RequiredArgsConstructor
public class FileSftpServiceImpl implements FileSftpService {

    private final NybgftpConfig nybgftpConfig;

    @Value("${template.path}")
    private String templatePath;

    private final JsonFileUtil jsonFileUtil;
    private final Scheduler scheduler;
    private final SftpUtil sftpUtil;
    private final FtpUtil ftpUtil;

    @Override
    public List<FileTransferConfig> getAllConfigs() {
        List<FileTransferConfig> fileTransferConfigs = jsonFileUtil.readConfigs();
        Collections.sort(fileTransferConfigs, Comparator.comparing(FileTransferConfig::getCreatedAt));
        return fileTransferConfigs;
    }

    @Override
    public FileTransferConfig getConfigById(String id) {
        return jsonFileUtil.readConfigs().stream()
                .filter(config -> config.getId().equals(id))
                .findFirst()
                .orElse(null);
    }

    @Override
    public FileTransferConfig saveConfig(FileTransferConfig config) throws Exception {
        List<FileTransferConfig> configs = jsonFileUtil.readConfigs();
        List<FileTransferConfig> newConfigs = new ArrayList<>(configs);
        if (config.getId() == null) {
            config.setId(UUID.randomUUID().toString());
            config.setCreatedAt(LocalDateTime.now());
            config.setPassword(AESEncryption.encrypt(config.getPassword()));
            newConfigs.add(config);
        } else {
            FileTransferConfig oldFileTransferConfig =
                    newConfigs.stream().filter(c -> c.getId().equals(config.getId())).findFirst().orElse(null);
            if (oldFileTransferConfig != null && !oldFileTransferConfig.getPassword().equals(config.getPassword())) {
                config.setPassword(AESEncryption.encrypt(config.getPassword()));
            }
            newConfigs.removeIf(c -> c.getId().equals(config.getId()));
            config.setUpdatedAt(LocalDateTime.now());
            newConfigs.add(config);
        }
       try {
           rescheduleJob(config);
       }catch (Exception e){
           e.printStackTrace();
           return config;
       }
        jsonFileUtil.writeConfigs(newConfigs);
       return config;
    }

    @Override
    public void deleteConfig(String id) {
        List<FileTransferConfig> configs = jsonFileUtil.readConfigs();
        configs.removeIf(config -> config.getId().equals(id));
        jsonFileUtil.writeConfigs(configs);

        try {
            scheduler.deleteJob(new JobKey(id, "file-transfer-jobs"));
        } catch (SchedulerException e) {
            throw new RuntimeException("Failed to delete job", e);
        }
    }

    @Override
    public void triggerJob(String id) {
        FileTransferConfig config = getConfigById(id);
        try {
            config.setPassword(AESEncryption.decrypt(config.getPassword()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (config == null) {
            return;
        }
        String csvFileDate = config.getCsvFileDate();
        LocalDate date = LocalDate.now();
        if (csvFileDate != null && !csvFileDate.isEmpty()){
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            date = LocalDate.parse(csvFileDate,formatter);
        }
        try {
            if ("SFTP_DOWNLOAD".equals(config.getType())) {
                sftpUtil.sftpDownloadFile(config,date);
            } else if ("SFTP_UPLOAD".equals(config.getType())) {
                sftpUtil.sftpUploadFile(config,date);
            }else if ("FTP_DOWNLOAD".equals(config.getType())) {
                ftpUtil.ftpDownloadFile(config,date);
            }else if ("FTP_UPLOAD".equals(config.getType())) {
                ftpUtil.ftpUploadFile(config,date);
            }
        } catch (Exception e) {
            throw new RuntimeException("File transfer failed", e);
        }
    }

    @Override
    public void rescheduleAllJobs() {
        List<FileTransferConfig> configs = getAllConfigs();
        configs.forEach(this::rescheduleJob);
    }

    private void rescheduleJob(FileTransferConfig config) {
        try {
            JobKey jobKey = new JobKey(config.getId(), "file-transfer-jobs");
            if (scheduler.checkExists(jobKey)) {
                scheduler.deleteJob(jobKey);
            }

            if (config.getActive()) {
                JobDetail jobDetail = JobBuilder.newJob(FileTransferJob.class)
                        .withIdentity(jobKey)
                        .usingJobData("configId", config.getId())
                        .build();

                Trigger trigger = TriggerBuilder.newTrigger()
                        .withIdentity(config.getId() + "-trigger", "file-transfer-triggers")
                        .withSchedule(CronScheduleBuilder.cronSchedule(config.getCronExpression()))
                        .build();

                scheduler.scheduleJob(jobDetail, trigger);
            }
        } catch (SchedulerException e) {
            throw new RuntimeException("Failed to schedule job", e);
        }catch (Exception e){
            throw new RuntimeException("Failed to decrypt password", e);
        }
    }


    /**
     * 下载用户手册
     */
    @Override
    public void downloadUserManual(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 用户手册路径
        log.info("templatePath:{}",templatePath);
        String filePath = templatePath+ "/user_manual.pdf";
        File file = new File(filePath);

        if (!file.exists()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        response.setContentType("application/pdf");
//        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Content-Disposition", "attachment; filename=\"user_manual.pdf\"");
        response.setContentLength((int) file.length());
        try (InputStream in = Files.newInputStream(file.toPath());
             OutputStream out = response.getOutputStream()) {

            byte[] buffer = new byte[1024];
            int length;
            while ((length = in.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }
            out.flush();
        }catch (IOException e){
            log.error("Download file error", e);
        }
    }
}
